<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8" />
    <!-- <meta http-equiv=X-UA-Compatible content="IE=edge"> -->
    <!-- <meta name="viewport" content="width=device-width, initial-scale=1" /> -->
    <meta name="viewport" content="width=480, initial-scale=1, maximum-scale=1, user-scalable=no">
    
    <title>和奥特曼合照</title>
    <!-- <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs"></script> -->
    <script src="./script/tf@3.29.1.min.js"></script>    
    <!-- <script src="https://cdn.jsdelivr.net/npm/@tensorflow-models/body-pix"></script> -->
    <script src="./script/body-pix@2.2.0.min.js"></script>
</head>
<body>
    <!-- <button id="startButton">开始 - Start Body Segmentation</button> -->
    <button id="startButton">开始</button>
    <button id="btnTakePhoto">拍照</button>
    <button id="btnNextVideo">切换视频</button>
    <div>
        <video id="video" width="50" height="50" style="display: none;" autoplay muted></video>
        <video id="videoBackground" src="" style="visibility: hidden;" width="50" height="50" loop></video>
    </div>

    <div style="height: 720px;">
        <canvas id="backgroundImage"
        style="position: absolute;
        z-index: 0;
        top: 40px;
        left: 0;"></canvas>

        <canvas id="outputCanvas"
        style="position: absolute;
        z-index: 99;
        top: 40px;
        left: 0;"></canvas>        
    </div>

    <p>相册:</p>
    <div id="photoArr"></div>

    <div style="height: 720px; visibility: hidden;">
        <p>finalCanvas:</p>
        <canvas id="finalCanvas">
        </canvas>
    </div>

	<script src="./script/eruda@3.4.1.js"></script>
	<!-- <script src="https://cdn.jsdelivr.net/npm/eruda"></script> -->
	<script>
		// 将 eruda 的初始化置于最前, 以获取浏览器的操作记录
		eruda.init();
	</script>

    <script>
        var thisDeviceCameraWidth = 0;
        var thisDeviceCameraHeight = 0;

        var currentVideoBackground_Width = 0;
        var currentVideoBackground_Height = 0;

        var videoFilesIndex = 0;
        
        // 测试不同规格的视频和摄像头, 计算他们谁最大, 用他们最大的值作为最终的 canvas 的宽和高
        // const videoFiles = [
        //     '/static/video/超人Z_08.mp4',
        //     // '/static/video/超人Z_09.mp4',
        //     // '/static/video/超人Z_13.mp4',
        //     '/static/video/超人迪迦_02.mkv', 
        //     '/static/video/成语动画廊_012.mp4',
        // ];

        const videoFiles = [            
            'static/video/雪拉比.mp4',    
            'static/video/超人Z.mp4',
        ];        

        let videoBackground = document.getElementById('videoBackground');
        
        videoBackground.onloadedmetadata = function() {
            console.debug("onloadedmetadata (视频加载完成):", videoBackground.videoWidth, videoBackground.videoHeight);
            currentVideoBackground_Width = videoBackground.videoWidth;
            currentVideoBackground_Height = videoBackground.videoHeight;
            console.debug("当前视频分辨率 (Current video resolution):", currentVideoBackground_Width, currentVideoBackground_Height);

            set_finalCanvas_Width_Height();

            videoBackground.play();
        };

        const videoBackgroundCanvas = document.getElementById('backgroundImage');
        const videoBackgroundCtx = videoBackgroundCanvas.getContext('2d');
        
        function set_finalCanvas_Width_Height()
        {
            // finalCanvas 的宽和高需要先用视频的宽高和摄像头的宽高进行比较, 取最大值
            finalCanvas.width = Math.max(currentVideoBackground_Width, thisDeviceCameraWidth);
            finalCanvas.height = Math.max(currentVideoBackground_Height, thisDeviceCameraHeight);

            videoBackgroundCanvas.width = currentVideoBackground_Width;
            videoBackgroundCanvas.height = currentVideoBackground_Height;
        }

        function drawFrame() 
        {
            if (!videoBackground.paused && !videoBackground.ended) {                
                // 将当前视频帧绘制到画布上
                videoBackgroundCtx.drawImage(videoBackground, 0, 0, currentVideoBackground_Width, currentVideoBackground_Height);

                console.log('drawImage')
            }
            // 请求在下一个动画帧调用drawFrame
            requestAnimationFrame(drawFrame);
        }
        
        videoBackground.addEventListener('play', ()=> {
            videoBackgroundCanvas.width = currentVideoBackground_Width;
            videoBackgroundCanvas.height = currentVideoBackground_Height;
            
            drawFrame();
        });

        let video = document.getElementById('video');
        let canvas = document.getElementById('outputCanvas');
        let ctx = canvas.getContext('2d');       

        async function startVideo() {
            const stream = await navigator.mediaDevices.getUserMedia({ video: true });
            video.srcObject = stream;

            // 获取摄像头分辨率
            const videoTrack = stream.getVideoTracks()[0];
            const settings = videoTrack.getSettings();
            console.debug("摄像头分辨率 (Camera resolution): ", settings.width, settings.height);

            // 动态设置 video 的 width 和 height
            video.width = settings.width;
            video.height = settings.height;

            thisDeviceCameraWidth = settings.width;
            thisDeviceCameraHeight = settings.height;
        }

        async function initializeBodyPix() {
            try {
                net = await bodyPix.load({
                    architecture: 'MobileNetV1',
                    outputStride: 16,
                    multiplier: 0.75,
                    quantBytes: 2
                });

                console.log('BodyPix model loaded successfully.');
            } catch (error) {
                console.error('Failed to load BodyPix model:', error);
            }
        }

        let updating = true;
        let buffer = null;

        async function segmentBody() {
            if (!net) {
                console.warn('BodyPix model is not loaded yet.');
                return;
            }

            const { data:map } = await net.segmentPerson(video, {internalResolution: 'medium'});
            // console.log('calc map:', map);

            updating = true;
            ctx.drawImage(video, 0, 0, video.videoWidth, video.videoHeight); // 不能省略, 先将图像全部画好, 然后更新 imgData

            const { data:imgData } = ctx.getImageData(0, 0, canvas.width, canvas.height);

            const newImg = ctx.createImageData(canvas.width, canvas.height);
            const newImgData = newImg.data;

            const whiteColor = [0, 0, 0, 0]; // 透明

            for(let i=0; i<map.length; i++) 
            {
                if(map[i])
                {
                    // 当前像数是人, 保留当前的颜色
                    [newImgData[i*4], newImgData[i*4+1], newImgData[i*4+2], newImgData[i*4+3]] = [imgData[i*4], imgData[i*4+1], imgData[i*4+2], imgData[i*4+3]];
                }
                else
                {
                    // 当前像数不是人, 设置为透明
                    [newImgData[i*4], newImgData[i*4+1], newImgData[i*4+2], newImgData[i*4+3]] = whiteColor;
                }
            }
            
            // console.log('draw mask');            
            ctx.putImageData(newImg, 0, 0);
            updating = false;

            requestAnimationFrame(segmentBody); // 重复此过程以实现实时效果
        }

        document.getElementById('startButton').addEventListener('click', async () => {
            console.log(`window.innerWH: ${window.innerWidth}, ${window.innerHeight}`);

            await initializeBodyPix();
            await startVideo();
            await video.play();

            canvas.width = video.videoWidth;
            canvas.height = video.videoHeight;

            // 先开启摄像头, 然后再播放视频, 确保能获取到摄像头的分辨率
            videoBackground.src = videoFiles[0]; // "/static/video/雪拉比.mp4";

            segmentBody();
        });
        
        const finalCanvas = document.getElementById('finalCanvas');
        const finalCanvasCtx = finalCanvas.getContext('2d');

        var photoArr = document.getElementById('photoArr');
        
        document.getElementById('btnTakePhoto').addEventListener('click', () => {
            if(updating) 
            {
                console.log('不符合要求, 跳出');
                return;
            }

            finalCanvasCtx.drawImage(videoBackground, 0, 0, currentVideoBackground_Width, currentVideoBackground_Height);
            finalCanvasCtx.drawImage(canvas, 0, 0, thisDeviceCameraWidth, thisDeviceCameraHeight);

            // 动态创建一个 img 标签
            const img = document.createElement('img');
            let dataURL = finalCanvas.toDataURL('image/png');
            img.src = dataURL;
            img.style.width = '100px';
            img.style.height = 'auto';
            img.style.marginLeft = '10px';

            // 添加点击事件，下载图片
            img.addEventListener('click', () => {
                const confirmDownload = confirm('是否下载这张图片？'); // 弹出对话框
                    if (!confirmDownload)
                        return;                

                    const link = document.createElement('a');
                    link.href = dataURL;
                    const now = new Date();
                    const timestamp = now.toISOString().replace(/[-:.]/g, '').slice(0, 15); // 格式化日期时间
                    link.download = `photo_${timestamp}.png`;
                    link.click();
                });

            // 将 img 标签添加到 photoArr 中
            photoArr.appendChild(img);
        });
        
        // 切换视频
        var btnNextVideo = document.getElementById('btnNextVideo');
        btnNextVideo.addEventListener('click', () => {
            videoFilesIndex++;
            if(videoFilesIndex >= videoFiles.length) {
                videoFilesIndex = 0;
            }
            // console.log(`切换视频: ${videoFiles[videoFilesIndex]}`);
            videoBackground.src = videoFiles[videoFilesIndex];
        });

        // 监控键盘事件
        document.addEventListener('keydown', (event) => {
            // console.log(`Key pressed: ${event.key}, Code: ${event.code}`);
            switch(event.key) 
            {
                case 'Enter':
                case 'Unidentified': // 如果 event.key === 'Unidentified', 可能是 Joycon 的按键事件
                    document.getElementById('btnTakePhoto').click(); // 执行拍照
                    break;
                default:
                    break;
            }
        });
        
    </script>
</body>
</html>